﻿//////////////////////////////////////////////////////////////////////////////
// <copyright file="PluginLoader.cs" company="Venhan Technologies Pvt. Ltd.">
// (c) 2013 Venhan Technologies, Narayana Reddy Tera. All rights reserved.
// </copyright>
//////////////////////////////////////////////////////////////////////////////

namespace Venhan.Framework.PluginManagement
{
  using System;
  using System.Collections.Generic;
  using System.ComponentModel.Composition;
  using System.ComponentModel.Composition.Hosting;
  using System.Linq;

  /// <summary>
  /// The class for plugin loader implements IPluginLoader.
  /// </summary>
  /// <typeparam name="TPlugin">The type of the plugin.</typeparam>
  /// <typeparam name="TPluginData">The type of the plugin data.</typeparam>
  public class PluginLoader<TPlugin, TPluginData> : IPluginLoader<TPlugin, TPluginData>
    where TPlugin : IPlugin
    where TPluginData : IPluginMetaData
  {
    #region Private Fields

    /// <summary>
    /// The field which is container for composition of plugins.
    /// </summary>
    private CompositionContainer _container;

    /// <summary>
    /// The field holds plugin path.
    /// </summary>
    private string _pluginPath;

    /// <summary>
    /// The field that specifies whether to look into current assembly or not.
    /// </summary>
    private bool _lookInCurrentAssembly;

    /// <summary>
    /// The field holds all loaded plugins.
    /// </summary>
    [ImportMany]
    private IEnumerable<Lazy<TPlugin, TPluginData>> _plugins;

    #endregion

    #region Constructors

    /// <summary>
    /// Initializes a new instance of the <see cref="PluginLoader{TPlugin, TPluginData}" /> class.
    /// </summary>
    /// <param name="pluginPath">The plugin path.</param>
    public PluginLoader(string pluginPath)
      : this(pluginPath, true)
    {
      _plugins = null;
    }

    /// <summary>
    /// Initializes a new instance of the <see cref="PluginLoader{TPlugin, TPluginData}" /> class.
    /// </summary>
    /// <param name="pluginPath">The plugin path.</param>
    /// <param name="lookInCurrentAssembly">if set to <c>true</c> [look in current assembly].</param>
    /// <exception cref="System.ArgumentNullException">pluginPath</exception>
    public PluginLoader(string pluginPath, bool lookInCurrentAssembly)
    {
      #region Pre-conditions

      if (string.IsNullOrWhiteSpace(pluginPath)) { throw new ArgumentNullException("pluginPath"); }

      #endregion

      _pluginPath = pluginPath;
      _lookInCurrentAssembly = lookInCurrentAssembly;
    }

    #endregion

    #region Private Methods

    /// <summary>
    /// Loads the plugins.
    /// </summary>
    private void LoadPlugins()
    {
      var catalog = new AggregateCatalog();
      if (_lookInCurrentAssembly)
      {
        catalog.Catalogs.Add(new AssemblyCatalog(this.GetType().Assembly));
      }
      catalog.Catalogs.Add(new DirectoryCatalog(_pluginPath));

      _container = new CompositionContainer(catalog);

      try
      {
        this._container.ComposeParts(this);
      }
      catch (CompositionException)
      {
        //TODO: log exception
        throw;
      }
    }

    #endregion

    #region IPluginLoader<TPluginType,TPluginData> Members

    /// <summary>
    /// Gets the plugins.
    /// </summary>
    /// <returns>
    /// List of Plugins.
    /// </returns>
    public IEnumerable<LazyPlugin<TPlugin, TPluginData>> GetPlugins()
    {
      LoadPlugins();

      IEnumerable<LazyPlugin<TPlugin, TPluginData>> plugins = from p in _plugins
                                                              select new LazyPlugin<TPlugin, TPluginData>(p);

      return plugins;
    }

    #endregion
  }
}
